package com.java.oplog.aspect;


import com.google.gson.Gson;
import com.java.oplog.anno.OpLogAnnotation;
import com.java.oplog.domain.OpLog;
import com.java.oplog.util.DateUtils;
import com.java.oplog.util.LogUtils;
import com.java.oplog.util.RequestHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;


/**
 * @author cf
 */
@Slf4j
@Aspect
@Component
public class OpLogAspects {

//    @Resource
//    private OpLogService opLogService;

    /**
     * 这里我们使用注解的形式
     * 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     */
    @Pointcut("@annotation(com.java.oplog.anno.OpLogAnnotation)")
    public void logPointCut() {
    }

    /**
     * 环绕通知 @Around  ， 当然也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName();
        Object[] args = joinPoint.getArgs();
        //数据统一处理
        OpLog opLog = dealData(method, methodName, args);
        //日志状态
        opLog.setStatus(1);
//        保存
//        opLogService.save(opLog);
        return result;
    }


    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName();
        Object[] args = joinPoint.getArgs();

        //数据统一处理
        OpLog opLog = dealData(method, methodName, args);
        //异常记录
        opLog.setExceptionDetails(LogUtils.getStackTrace(e));
        //日志状态
        opLog.setStatus(0);
        //        保存
//        opLogService.save(opLog);
    }


    /**
     * 配置公共日志数据处理
     *
     * @param method     方法体
     * @param methodName 方法名
     * @param args       参数信息
     * @return 返回日志结果
     */
    private OpLog dealData(Method method, String methodName, Object[] args) {
        OpLog opLog = new OpLog();
        long startTime = System.currentTimeMillis();
        opLog.setExeuTime(System.currentTimeMillis() - startTime);


        //创建日期
        opLog.setCreateTime(DateUtils.formatLocalDateTimeForString(LocalDateTime.now()));

        //注解上的描述
        OpLogAnnotation logs = method.getAnnotation(OpLogAnnotation.class);
        if (logs != null) {
            opLog.setTitle(logs.name());
        }
        //请求的 类名、方法名
        opLog.setMethod(methodName);
        //获取请求的参数,用户名称，ip,浏览器信息
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        //文件上传类型，则不存储
        LogUtils.getContentType(request);
        if (!LogUtils.getContentType(request)) {
            try {
                List<String> list = new ArrayList<String>();
                for (Object o : args) {
                    list.add(new Gson().toJson(o));
                }
                opLog.setParams(list.toString());
            } catch (Exception e) {
                log.error("请求参数解析失败,错误信息：" + e);
            }
        }
        opLog.setIp(LogUtils.getIp(request));
        opLog.setBrowser(LogUtils.getBrowser(request));
        opLog.setMethodType(LogUtils.getMethodType(request));
        return opLog;
    }


}
